package cn.com.zcode.core.scan;

import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.log4j.Logger;

/**
 * 
 * @author zeng.jinqing
 * @date Apr 19, 2013
 */
public class Scanner {
	private Logger logger = Logger.getLogger(Scanner.class);
	private ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

	public <T> Set<T> scan(ScanFilter<T> filter, String... packages) {
		Set<T> classes = new HashSet<T>();
		for (String prstr : packages) {
			prstr = prstr.trim();
			int pos = prstr.indexOf("*");
			String packageName = prstr;
			String ruler = "^.*$";
			if (pos != -1) {
				int pp = prstr.lastIndexOf(".", pos);
				pp = pp == -1 ? pos : pp;
				packageName = prstr.substring(0, pp);
				ruler = prstr.replace(".", "\\.").replace("*", ".*");
			}

			logger.info("Scann Ruler[" + ruler + "],Root package:[" + packageName + "]");
			String path = packageName.replace('.', '/');
			try {
				Enumeration<URL> urls = classLoader.getResources(path);
				while (urls.hasMoreElements()) {
					URL url = urls.nextElement();
					String protocol = url.getProtocol();
					if ("file".equals(protocol)) {
						String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
						logger.info("Scanning File directory:=>" + filePath);
						File file = new File(filePath);
						classes.addAll(this.findInClasses(file,packageName, filter));
					} else if ("jar".equals(protocol)) {
						logger.info("Scanning JAR file:=>" + url);
						classes.addAll(this.findInJars(url, packageName, filter));
					}
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return classes;
	}

	private <T> Collection<T> findInJars(URL url, String packageName, ScanFilter<T> filter) {
		Set<T> xmlFiles = new HashSet<T>();
		try {
			JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
			Enumeration<JarEntry> entries = jar.entries();
			while (entries.hasMoreElements()) {
				JarEntry entry = entries.nextElement();
				String fileName = entry.getName();
				T t = filter.pass(entry, packageName);
				if (t != null) {
					xmlFiles.add(t);
				}

			}
		} catch (IOException e) {

			e.printStackTrace();
		}
		return xmlFiles;
	}

	private <T> Set<T> findInClasses(File directory, String packageName, ScanFilter<T> filter) {

		Set<T> fileSet = new HashSet<T>();
		if (directory.exists()) {
			packageName = packageName.isEmpty() ? "" : packageName + ".";
			File[] files = directory.listFiles();
			for (File file : files) {
				String fileName = file.getName();
				if (file.isDirectory()) {
					Set<T> subclasses = this.findInClasses(file, packageName + fileName, filter);
					fileSet.addAll(subclasses);
				} else {
					T t = filter.pass(file, packageName);
					if (t != null) {
						fileSet.add(t);
					}
				}
			}
		} else {
			logger.warn("Directory no exists:" + directory);
		}
		return fileSet;
	}

}
